#include <string.h>

#include "lua.h"
#include "lauxlib.h"

#define  UC unsigned char
/*-------------------------------------------------------------------------*\
* Base64 globals
\*-------------------------------------------------------------------------*/
static const UC b64base[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static UC b64unbase[256];

static void b64setup(UC *b64unbase) 
{
	int i;
	for (i = 0; i <= 255; i++) b64unbase[i] = (UC) 255;
	for (i = 0; i < 64; i++) b64unbase[b64base[i]] = (UC) i;
	b64unbase['='] = 0;
}

static size_t b64decode(UC c, UC *input, size_t size, 
						UC *buffer, size_t* len )
{
	/* ignore invalid characters */
	if (b64unbase[c] > 64) return size;
	input[size++] = c;
	/* decode atom */
	if (size == 4) {
		UC decoded[3];
		int valid, value = 0;
		value =  b64unbase[input[0]]; value <<= 6;
		value |= b64unbase[input[1]]; value <<= 6;
		value |= b64unbase[input[2]]; value <<= 6;
		value |= b64unbase[input[3]];
		decoded[2] = (UC) (value & 0xff); value >>= 8;
		decoded[1] = (UC) (value & 0xff); value >>= 8;
		decoded[0] = (UC) value;
		/* take care of paddding */
		valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3; 
		//luaL_addlstring(buffer, (char *) decoded, valid);
		memcpy(buffer+(*len), (char*)decoded, valid);
		*len += valid;
		return 0;
		/* need more data */
	} else return size;
}

static int moddecode(lua_State *L)
{
	UC atom[4];
	size_t isize = 0, asize = 0;
	const char *k = lua_tostring(L, 1);
	const UC *input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
	const UC *last = input + isize;
	UC* buffer = NULL;
	size_t len = 0;
	int ret = 0;
	/* end-of-input blackhole */
	if (!input) {
		lua_pushnil(L);
		lua_pushnil(L);
		return 2;
	}
	buffer = malloc(isize);
	if (buffer == NULL)
	{
		lua_pushnil(L);
		lua_pushnil(L);
		return 2;
	}
	/* process first part of the input */
	while (input < last) 
		asize = b64decode(*input++, atom, asize, buffer, &len);
	ret = luaL_loadbuffer(L,buffer,len,k);
	free(buffer);
	return ret==0 ? 1:0;
}
void moddecode_init(lua_State *L)
{
	b64setup(b64unbase);
	lua_register(L, "moddecode_b64", moddecode);
}
